import StyleSheet, {
  type ColorValue,
} from 'react-native/Libraries/StyleSheet/StyleSheet';
import type { SyntheticEvent } from "react-native/Libraries/Types/CoreEventTypes";
import type { ViewProps } from 'react-native/Libraries/View/ViewPropTypes';
import useMergeRefs from 'react-native/Libraries/Utilities/useMergeRefs';

import * as React from 'react';
import SwitchNativeComponent, {
  Commands as SwitchCommands,
} from './NativeSwitchComponentHarmony';
type SwitchChangeEvent = SyntheticEvent<
  $ReadOnly<{|
    value: boolean,
    target: number,
  |}>,
>;

export type Props = $ReadOnly<{|
  ...ViewProps,

  /**
    If true the user won't be able to toggle the switch.

    @default false
   */
  disabled?: ?boolean,

  /**
    The value of the switch. If true the switch will be turned on.

    @default false
   */
  value?: ? boolean,

  /**
    Color of the foreground switch grip. If this is set on iOS, the switch grip will lose its drop shadow.
   */
  thumbColor ?: ? ColorValue,

  tintColor ?: ? ColorValue,

  onTintColor ?: ? ColorValue,

  /**
    Custom colors for the switch track.

    _iOS_: When the switch value is false, the track shrinks into the border. If you want to change the
    color of the background exposed by the shrunken track, use
     [`ios_backgroundColor`](https://reactnative.dev/docs/switch#ios_backgroundColor).
   */
  trackColor ?: ? $ReadOnly < {|
    false ?: ? ColorValue,
  true ?: ? ColorValue,
  |}>,

  /**
    On iOS, custom color for the background. This background color can be
    seen either when the switch value is false or when the switch is
    disabled (and the switch is translucent).
   */
  ios_backgroundColor ?: ? ColorValue,

  /**
    Invoked when the user tries to change the value of the switch. Receives
    the change event as an argument. If you want to only receive the new
    value, use `onValueChange` instead.
   */
  onChange ?: ? (event: SwitchChangeEvent) => Promise<void> | void,

  /**
    Invoked when the user tries to change the value of the switch. Receives
    the new value as an argument. If you want to instead receive an event,
    use `onChange`.
   */
  onValueChange ?: ? (value: boolean) => Promise<void> | void,
|}>;
const returnsFalse = () => false;
const returnsTrue = () => true;



const SwitchWithForwardedRef: React.AbstractComponent<
  Props,
  React.ElementRef<
    typeof SwitchNativeComponent,
  >,
> = React.forwardRef(function Switch(props, forwardedRef): React.Node {
  const {
    disabled,
    ios_backgroundColor,
    onChange,
    onValueChange,
    style,
    thumbColor,
    tintColor,
    value,
    onTintColor,
    ...restProps
  } = props;

  const nativeSwitchRef = React.useRef(null);

  const ref = useMergeRefs(nativeSwitchRef, forwardedRef);

  const [native, setNative] = React.useState({ value: (null: ?boolean)
});

const handleChange = (event: SwitchChangeEvent) => {
  // $FlowFixMe[unused-promise]
  onChange?.(event);
  // $FlowFixMe[unused-promise]
  onValueChange?.(event.nativeEvent.value);
  setNative({ value: event.nativeEvent.value });
};

React.useLayoutEffect(() => {
  // This is necessary in case native updates the switch and JS decides
  // that the update should be ignored and we should stick with the value
  // that we have in JS.
  const jsValue = value === true;
  const shouldUpdateNativeSwitch =
    native.value != null && native.value !== jsValue;
  if (
    shouldUpdateNativeSwitch &&
    nativeSwitchRef.current?.setNativeProps != null
  ) {
    SwitchCommands.setValue(nativeSwitchRef.current, jsValue);
  }
}, [value, native]);


const platformProps = {
  disabled,
  onTintColor: onTintColor,
  style: StyleSheet.compose(
    { height: 31, width: 51 },
    StyleSheet.compose(
      style,
      ios_backgroundColor == null
        ? null
        : {
          backgroundColor: ios_backgroundColor,
          borderRadius: 16,
        },
    ),
  ),
  thumbTintColor: thumbColor,
  tintColor: tintColor,
  value: value === true,
};

return (
  <SwitchNativeComponent
        { ...restProps }
        {...platformProps }
        accessibilityRole = { props.accessibilityRole ?? 'switch' }
        onChange = { handleChange }
        onResponderTerminationRequest = { returnsFalse }
        onStartShouldSetResponder = { returnsTrue }
        ref = { ref }
  />
    );
});

export default SwitchWithForwardedRef;
